home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / Java2 / src / javax / swing / JTabbedPane.java < prev    next >
Encoding:
Java Source  |  1999-05-28  |  42.6 KB  |  1,386 lines  |  [TEXT/CWIE]

  1. /*
  2.  * %W% %E%
  3.  *
  4.  * Copyright 1997, 1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package javax.swing;
  16.  
  17. import java.awt.*;
  18. import java.awt.event.*;
  19. import java.beans.*;
  20. import java.util.*;
  21. import javax.swing.event.*;
  22. import javax.swing.plaf.*;
  23. import javax.accessibility.*;
  24.  
  25. import java.io.Serializable; 
  26. import java.io.ObjectOutputStream;
  27. import java.io.ObjectInputStream;
  28. import java.io.IOException;
  29.  
  30.  
  31. /**
  32.  * A component which lets the user switch between a group of components by
  33.  * clicking on a tab with a given title and/or icon.
  34.  * <p>
  35.  * Tabs/components are added to a TabbedPane object by using the addTab and
  36.  * insertTab methods.  A tab is represented by an index corresponding
  37.  * to the position it was added in, where the first tab has an index equal to 0
  38.  * and the last tab has an index equal to the tab count minus 1.
  39.  * <p>
  40.  * The TabbedPane uses a SingleSelectionModel to represent the set
  41.  * of tab indeces and the currently selected index.  If the tab count 
  42.  * is greater than 0, then there will always be a selected index, which
  43.  * by default will be initialized to the first tab.  If the tab count is
  44.  * 0, then the selected index will be -1.
  45.  * <p>
  46.  * See <a href="http://java.sun.com/docs/books/tutorial/ui/swing/tabbedpane.html">How to Use Tabbed Panes</a>
  47.  * in <a href="http://java.sun.com/Series/Tutorial/index.html"><em>The Java Tutorial</em></a>
  48.  * for further documentation.
  49.  * <p>
  50.  * For the keyboard keys used by this component in the standard Look and
  51.  * Feel (L&F) renditions, see the
  52.  * <a href="doc-files/Key-Index.html#JTabbedPane">JTabbedPane</a> key assignments.
  53.  * <p>
  54.  * <strong>Warning:</strong>
  55.  * Serialized objects of this class will not be compatible with 
  56.  * future Swing releases.  The current serialization support is appropriate
  57.  * for short term storage or RMI between applications running the same
  58.  * version of Swing.  A future release of Swing will provide support for
  59.  * long term persistence.
  60.  *
  61.  * @beaninfo
  62.  *      attribute: isContainer true
  63.  *    description: A component which provides a tab folder metaphor for 
  64.  *                 displaying one component from a set of components.
  65.  *
  66.  * @version %I% %G%
  67.  * @author Dave Moore
  68.  * @author Philip Milne
  69.  * @author Amy Fowler
  70.  *
  71.  * @see SingleSelectionModel
  72.  */
  73. public class JTabbedPane extends JComponent 
  74.        implements Serializable, Accessible, SwingConstants {
  75.  
  76.     /**
  77.      * @see #getUIClassID
  78.      * @see #readObject
  79.      */
  80.     private static final String uiClassID = "TabbedPaneUI";
  81.  
  82.     /** 
  83.      * Where the tabs are placed.
  84.      * @see #setTabPlacement
  85.      */
  86.     protected int tabPlacement = TOP;
  87.     /** The default selection model */
  88.     protected SingleSelectionModel model;
  89.  
  90.     private boolean haveRegistered;
  91.  
  92.     /**
  93.      * The changeListener is the listener we add to the
  94.      * model.  
  95.      */
  96.     protected ChangeListener changeListener = null;
  97.  
  98.     Vector pages;
  99.  
  100.     /**
  101.      * Only one ChangeEvent is needed per TabPane instance since the
  102.      * event's only (read-only) state is the source property.  The source
  103.      * of events generated here is always "this".
  104.      */
  105.     protected transient ChangeEvent changeEvent = null;
  106.  
  107.     /**
  108.      * Creates an empty TabbedPane.
  109.      * @see #addTab
  110.      */
  111.     public JTabbedPane() {
  112.         this(TOP);
  113.     }
  114.  
  115.     /**
  116.      * Creates an empty TabbedPane with the specified tab placement
  117.      * of either: TOP, BOTTOM, LEFT, or RIGHT.
  118.      * @param tabPlacement the placement for the tabs relative to the content
  119.      * @see #addTab
  120.      */
  121.     public JTabbedPane(int tabPlacement) {
  122.         setTabPlacement(tabPlacement);
  123.         pages = new Vector(1);
  124.         setModel(new DefaultSingleSelectionModel());
  125.         updateUI();
  126.     }
  127.  
  128.     /**
  129.      * Returns the UI object which implements the L&F for this component.
  130.      * @see #setUI
  131.      */
  132.     public TabbedPaneUI getUI() {
  133.         return (TabbedPaneUI)ui;
  134.     }
  135.  
  136.     /**
  137.      * Sets the UI object which implements the L&F for this component.
  138.      * @param ui the new UI object
  139.      * @see UIDefaults#getUI
  140.      * @beaninfo
  141.      *        bound: true
  142.      *       hidden: true
  143.      *    attribute: visualUpdate true
  144.      *  description: The UI object that implements the tabbedpane's LookAndFeel
  145.      */
  146.     public void setUI(TabbedPaneUI ui) {
  147.         super.setUI(ui);
  148.     }
  149.  
  150.     /**
  151.      * Notification from the UIManager that the L&F has changed. 
  152.      * Called to replace the UI with the latest version from the 
  153.      * default UIFactory.
  154.      *
  155.      * @see JComponent#updateUI
  156.      */
  157.     public void updateUI() {
  158.         setUI((TabbedPaneUI)UIManager.getUI(this));
  159.     }
  160.  
  161.  
  162.     /**
  163.      * Returns the name of the UI class that implements the
  164.      * L&F for this component.
  165.      *
  166.      * @return "TabbedPaneUI"
  167.      * @see JComponent#getUIClassID
  168.      * @see UIDefaults#getUI
  169.      */
  170.     public String getUIClassID() {
  171.         return uiClassID;
  172.     }
  173.  
  174.  
  175.     /**
  176.      * We pass ModelChanged events along to the listeners with
  177.      * the tabbedpane (instead of the model itself) as the event source.
  178.      */
  179.     protected class ModelListener implements ChangeListener, Serializable {
  180.         public void stateChanged(ChangeEvent e) {
  181.             fireStateChanged();            
  182.         }       
  183.     }
  184.  
  185.     /**
  186.      * Subclasses that want to handle ChangeEvents differently
  187.      * can override this to return a subclass of ModelListener or
  188.      * another ChangeListener implementation.
  189.      * 
  190.      * @see #fireStateChanged
  191.      */
  192.     protected ChangeListener createChangeListener() {
  193.         return new ModelListener();
  194.     }
  195.  
  196.     /**
  197.      * Adds a ChangeListener to this tabbedpane.
  198.      *
  199.      * @param l the ChangeListener to add
  200.      * @see #fireStateChanged
  201.      * @see #removeChangeListener
  202.      */
  203.     public void addChangeListener(ChangeListener l) {
  204.         listenerList.add(ChangeListener.class, l);
  205.     }
  206.     
  207.     /**
  208.      * Removes a ChangeListener from this tabbedpane.
  209.      *
  210.      * @param l the ChangeListener to remove
  211.      * @see #fireStateChanged
  212.      * @see #addChangeListener
  213.      */
  214.     public void removeChangeListener(ChangeListener l) {
  215.         listenerList.remove(ChangeListener.class, l);
  216.     }
  217.         
  218.     /**
  219.      * Send a ChangeEvent, whose source is this tabbedpane, to
  220.      * each listener.  This method method is called each time 
  221.      * a ChangeEvent is received from the model.
  222.      * 
  223.      * @see #addChangeListener
  224.      * @see EventListenerList
  225.      */
  226.     protected void fireStateChanged() {
  227.         // Guaranteed to return a non-null array
  228.         Object[] listeners = listenerList.getListenerList();
  229.         // Process the listeners last to first, notifying
  230.         // those that are interested in this event
  231.         for (int i = listeners.length-2; i>=0; i-=2) {
  232.             if (listeners[i]==ChangeListener.class) {
  233.                 // Lazily create the event:
  234.                 if (changeEvent == null)
  235.                     changeEvent = new ChangeEvent(this);
  236.                 ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
  237.             }          
  238.         }
  239.     }   
  240.  
  241.     /**
  242.      * Returns the model associated with this tabbedpane.
  243.      *
  244.      * @see #setModel
  245.      */
  246.     public SingleSelectionModel getModel() {
  247.         return model;
  248.     }
  249.  
  250.     /**
  251.      * Sets the model to be used with this tabbedpane.
  252.      * @param model the model to be used
  253.      * 
  254.      * @see #getModel
  255.      * @beaninfo
  256.      *       bound: true
  257.      * description: The tabbedpane's SingleSelectionModel.
  258.      */
  259.     public void setModel(SingleSelectionModel model) {
  260.         SingleSelectionModel oldModel = getModel();
  261.  
  262.         if (oldModel != null) {
  263.             oldModel.removeChangeListener(changeListener);
  264.             changeListener = null;
  265.         }
  266.  
  267.         this.model = model;
  268.  
  269.         if (model != null) {
  270.             changeListener = createChangeListener();
  271.             model.addChangeListener(changeListener);
  272.         }
  273.  
  274.         firePropertyChange("model", oldModel, model);
  275.         repaint();
  276.     }
  277.  
  278.     /**
  279.      * Returns the placement of the tabs for this tabbedpane.
  280.      * @see #setTabPlacement
  281.      */
  282.     public int getTabPlacement() {
  283.         return tabPlacement;
  284.     }
  285.  
  286.     /**
  287.      * Sets the tab placement for this tabbedpane.
  288.      * Possible values are:<ul>
  289.      * <li>SwingConstants.TOP
  290.      * <li>SwingConstants.BOTTOM
  291.      * <li>SwingConstants.LEFT
  292.      * <li>SwingConstants.RIGHT
  293.      * </ul>
  294.      * The default value, if not set, is TOP.
  295.      *
  296.      * @param tabPlacement the placement for the tabs relative to the content
  297.      *
  298.      * @beaninfo
  299.      *    preferred: true
  300.      *        bound: true
  301.      *    attribute: visualUpdate true
  302.      *         enum: TOP JTabbedPane.TOP 
  303.      *               LEFT JTabbedPane.LEFT
  304.      *               BOTTOM JTabbedPane.BOTTOM
  305.      *               RIGHT JTabbedPane.RIGHT
  306.      *  description: The tabbedpane's tab placement.
  307.      *
  308.      */
  309.     public void setTabPlacement(int tabPlacement) {
  310.         if (tabPlacement != TOP && tabPlacement != LEFT && 
  311.             tabPlacement != BOTTOM && tabPlacement != RIGHT) {
  312.             throw new IllegalArgumentException("illegal tab placement: must be TOP, BOTTOM, LEFT, or RIGHT");
  313.         }
  314.         if (this.tabPlacement != tabPlacement) {
  315.             int oldValue = this.tabPlacement;
  316.             this.tabPlacement = tabPlacement;
  317.             firePropertyChange("tabPlacement", oldValue, tabPlacement);
  318.             revalidate();
  319.             repaint();
  320.         }
  321.     }
  322.  
  323.     /**
  324.      * Returns the currently selected index for this tabbedpane.
  325.      * Returns -1 if there is no currently selected tab.
  326.      *
  327.      * @return the index of the selected tab
  328.      * @see #setSelectedIndex
  329.      */    
  330.     public int getSelectedIndex() {
  331.         return model.getSelectedIndex();
  332.     }
  333.  
  334.     /**
  335.      * Sets the selected index for this tabbedpane.
  336.      *
  337.      * @see #getSelectedIndex
  338.      * @see SingleSelectionModel#setSelectedIndex
  339.      * @beaninfo
  340.      *   preferred: true
  341.      * description: The tabbedpane's selected tab index.
  342.      */
  343.     public void setSelectedIndex(int index) {
  344.         int oldIndex = model.getSelectedIndex();
  345.  
  346.         model.setSelectedIndex(index);
  347.  
  348.         if ((oldIndex >= 0) && (oldIndex != index)) {
  349.             Page oldPage = (Page) pages.elementAt(oldIndex);
  350.             if (accessibleContext != null) {
  351.                 accessibleContext.firePropertyChange(
  352.                         AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 
  353.                         AccessibleState.SELECTED, null);
  354.             }
  355.         }
  356.         if ((index >= 0) && (oldIndex != index)) {
  357.             Page newPage = (Page) pages.elementAt(index);
  358.             if (accessibleContext != null) {
  359.                 accessibleContext.firePropertyChange(
  360.                         AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 
  361.                         null, AccessibleState.SELECTED);
  362.             }
  363.         }
  364.     }
  365.  
  366.     /**
  367.      * Returns the currently selected component for this tabbedpane.
  368.      * Returns null if there is no currently selected tab.
  369.      *
  370.      * @return the component corresponding to the selected tab
  371.      * @see #setSelectedComponent
  372.      */   
  373.     public Component getSelectedComponent() { 
  374.         int index = getSelectedIndex();
  375.         if (index == -1) {
  376.             return null;
  377.         }
  378.         return getComponentAt(index);
  379.     }
  380.  
  381.     /**
  382.      * Sets the selected component for this tabbedpane.  This
  383.      * will automatically set the selectedIndex to the index
  384.      * corresponding to the specified component.
  385.      *
  386.      * @see #getSelectedComponent
  387.      * @beaninfo
  388.      *   preferred: true
  389.      * description: The tabbedpane's selected component.
  390.      */
  391.     public void setSelectedComponent(Component c) {
  392.         int index = indexOfComponent(c);
  393.         if (index != -1) {
  394.             setSelectedIndex(index);
  395.         } else {
  396.             throw new IllegalArgumentException("component not found in tabbed pane");
  397.         }
  398.     }
  399.  
  400.     /**
  401.      * Inserts a <i>component</i>, at <i>index</i>, represented by a
  402.      * <i>title</i> and/or <i>icon</i>, either of which may be null. 
  403.      * Uses java.util.Vector internally, see insertElementAt() 
  404.      * for details of insertion conventions. 
  405.      * @param title the title to be displayed in this tab
  406.      * @param icon the icon to be displayed in this tab
  407.      * @param component The component to be displayed when this tab is clicked. 
  408.      * @param tip the tooltip to be displayed for this tab
  409.      * @param index the position to insert this new tab 
  410.      *
  411.      * @see #addTab
  412.      * @see #removeTabAt  
  413.      */
  414.     public void insertTab(String title, Icon icon, Component component, String tip, int index) {
  415.         Icon disabledIcon = null;
  416.         if (icon != null && icon instanceof ImageIcon) {
  417.             disabledIcon = new ImageIcon(
  418.                     GrayFilter.createDisabledImage(
  419.                         ((ImageIcon)icon).getImage()));
  420.         }
  421.         if (component != null) {
  422.             component.setVisible(false);
  423.             addImpl(component, null, -1);
  424.         }
  425.         pages.insertElementAt(new Page(this, title != null? title : "", icon, disabledIcon,
  426.                                        component, tip), index);
  427.  
  428.         if (pages.size() == 1) {
  429.             setSelectedIndex(0);
  430.         }
  431.         if (!haveRegistered && tip != null) {
  432.             ToolTipManager.sharedInstance().registerComponent(this);
  433.             haveRegistered = true;
  434.         }
  435.  
  436.         if (accessibleContext != null) {
  437.             accessibleContext.firePropertyChange(
  438.                     AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 
  439.                     null, component);
  440.         }
  441.         revalidate();
  442.         repaint();
  443.     }
  444.  
  445.     /**
  446.      * Adds a <i>component</i> and <i>tip</i> represented by a <i>title</i> 
  447.      * and/or <i>icon</i>, either of which can be null. 
  448.      * Cover method for insertTab().
  449.      * @param title the title to be displayed in this tab
  450.      * @param icon the icon to be displayed in this tab
  451.      * @param component The component to be displayed when this tab is clicked. 
  452.      * @param tip the tooltip to be displayed for this tab
  453.      * 
  454.      * @see #insertTab
  455.      * @see #removeTabAt  
  456.      */
  457.     public void addTab(String title, Icon icon, Component component, String tip) {
  458.         insertTab(title, icon, component, tip, pages.size()); 
  459.     }
  460.  
  461.     /**
  462.      * Adds a <i>component</i> represented by a <i>title</i> and/or <i>icon</i>, 
  463.      * either of which can be null. 
  464.      * Cover method for insertTab(). 
  465.      * @param title the title to be displayed in this tab
  466.      * @param icon the icon to be displayed in this tab
  467.      * @param component The component to be displayed when this tab is clicked. 
  468.      *
  469.      * @see #insertTab
  470.      * @see #removeTabAt  
  471.      */
  472.     public void addTab(String title, Icon icon, Component component) {
  473.         insertTab(title, icon, component, null, pages.size()); 
  474.     }
  475.  
  476.     /**
  477.      * Adds a <i>component</i> represented by a <i>title</i> and no icon. 
  478.      * Cover method for insertTab().
  479.      * @param title the title to be displayed in this tab
  480.      * @param component The component to be displayed when this tab is clicked. 
  481.      * 
  482.      * @see #insertTab
  483.      * @see #removeTabAt  
  484.      */
  485.     public void addTab(String title, Component component) {
  486.         insertTab(title, null, component, null, pages.size()); 
  487.     }
  488.  
  489.     /**
  490.      * Adds a <i>component</i> with a tab title defaulting to
  491.      * the name of the component.
  492.      * Cover method for insertTab().
  493.      * @param component The component to be displayed when this tab is clicked. 
  494.      *
  495.      * @see #insertTab
  496.      * @see #removeTabAt  
  497.      */
  498.     public Component add(Component component) {
  499.         addTab(component.getName(), component);
  500.         return component;
  501.     }
  502.  
  503.     /**
  504.      * Adds a <i>component</i> with the specified tab title.
  505.      * Cover method for insertTab().
  506.      * @param title the title to be displayed in this tab
  507.      * @param component The component to be displayed when this tab is clicked.
  508.      *
  509.      * @see #insertTab
  510.      * @see #removeTabAt  
  511.      */
  512.     public Component add(String title, Component component) {
  513.         addTab(title, component);
  514.         return component;
  515.     }
  516.  
  517.     /**
  518.      * Adds a <i>component</i> at the specified tab index with a tab
  519.      * title defaulting to the name of the component.
  520.      * Cover method for insertTab().
  521.      * @param component The component to be displayed when this tab is clicked.
  522.      * @param index the position to insert this new tab 
  523.      *
  524.      * @see #insertTab
  525.      * @see #removeTabAt  
  526.      */
  527.     public Component add(Component component, int index) {
  528.         insertTab(component.getName(), null, component, null, index);
  529.         return component;
  530.     }
  531.  
  532.     /**
  533.      * Adds a <i>component</i> to the tabbed pane.  If constraints
  534.      * is a String or an Icon, it will be used for the tab title,
  535.      * otherwise the component's name will be used as the tab title. 
  536.      * Cover method for insertTab().
  537.      * @param component The component to be displayed when this tab is clicked. 
  538.      * @constraints the object to be displayed in the tab
  539.      *
  540.      * @see #insertTab
  541.      * @see #removeTabAt  
  542.      */
  543.     public void add(Component component, Object constraints) {
  544.         if (constraints instanceof String) {
  545.             addTab((String)constraints, component);
  546.         } else if (constraints instanceof Icon) {
  547.             addTab(null, (Icon)constraints, component);
  548.         } else {
  549.             add(component);
  550.         }
  551.     }
  552.  
  553.     /**
  554.      * Adds a <i>component</i> at the specified tab index.  If constraints
  555.      * is a String or an Icon, it will be used for the tab title,
  556.      * otherwise the component's name will be used as the tab title. 
  557.      * Cover method for insertTab().
  558.      * @param component The component to be displayed when this tab is clicked. 
  559.      * @constraints the object to be displayed in the tab
  560.      * @param index the position to insert this new tab 
  561.      *
  562.      * @see #insertTab
  563.      * @see #removeTabAt  
  564.      */
  565.     public void add(Component component, Object constraints, int index) {
  566.         Icon icon = constraints instanceof Icon? (Icon)constraints : null;
  567.         String title = constraints instanceof String? (String)constraints : null;
  568.         insertTab(title, icon, component, null, index);
  569.     }
  570.  
  571.     /**
  572.      * Removes the tab at <i>index</i>.
  573.      * After the component associated with <i>index</i> is removed,
  574.      * its visibility is reset to true to ensure it will be visible
  575.      * if added to other containers.
  576.      * @param index the index of the tab to be removed
  577.      *
  578.      * @see #addTab
  579.      * @see #insertTab  
  580.      */
  581.     public void removeTabAt(int index) {            
  582.         // If we are removing the currently selected tab AND
  583.         // it happens to be the last tab in the bunch, then
  584.         // select the previous tab
  585.         int tabCount = getTabCount();
  586.         int selected = getSelectedIndex();
  587.         if (selected >= (tabCount - 1)) {
  588.             setSelectedIndex(selected - 1);
  589.         }
  590.  
  591.         Component component = getComponentAt(index);
  592.  
  593.         if (accessibleContext != null) {
  594.             accessibleContext.firePropertyChange(
  595.                     AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 
  596.                     component, null);
  597.         }
  598.         if (component != null) {
  599.             super.remove(component);
  600.             component.setVisible(true);
  601.         }
  602.  
  603.         pages.removeElementAt(index);
  604.         revalidate();
  605.         repaint();
  606.     }
  607.  
  608.     /**
  609.      * Removes the tab which corresponds to the specified component.
  610.      *
  611.      * @param component the component to remove from the tabbedpane
  612.      * @see #addTab
  613.      * @see #removeTabAt  
  614.      */
  615.     public void remove(Component component) {
  616.         int index = indexOfComponent(component);
  617.         if (index != -1) {
  618.             removeTabAt(index);
  619.         }
  620.     }
  621.  
  622.     /**
  623.      * Removes all the tabs from the tabbedpane.
  624.      *
  625.      * @see #addTab
  626.      * @see #removeTabAt  
  627.      */
  628.     public void removeAll() {
  629.         setSelectedIndex(-1);
  630.  
  631.         int tabCount = getTabCount();
  632.         for (int i = 0; i < tabCount; i++) {
  633.             Component component = getComponentAt(i);
  634.             if (accessibleContext != null) {
  635.                 accessibleContext.firePropertyChange(
  636.                     AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 
  637.                     component, null);
  638.             }
  639.         }
  640.         super.removeAll();
  641.         pages.removeAllElements();
  642.         revalidate();
  643.         repaint();
  644.     }
  645.  
  646.     /**
  647.      * Returns the number of tabs in this tabbedpane.
  648.      *
  649.      * @return an int specifying the number of tabbed pages
  650.      */
  651.     public int getTabCount() {
  652.         return pages.size();
  653.     }
  654.  
  655.     /**
  656.      * Returns the number of tab runs currently used to display
  657.      * the tabs. 
  658.      * @return an int giving the number of rows if the tabPlacement
  659.      *         is TOP or BOTTOM and the number of columns if tabPlacement
  660.      *         is LEFT or RIGHT, or 0 if there is no UI set on this
  661.      *         tabbedpane
  662.      */
  663.     public int getTabRunCount() {
  664.         if (ui != null) {
  665.             return ((TabbedPaneUI)ui).getTabRunCount(this);
  666.         }
  667.         return 0;
  668.     }
  669.  
  670.  
  671. // Getters for the Pages
  672.  
  673.     /**
  674.      * Returns the tab title at <i>index</i>.
  675.      *
  676.      * @see #setTitleAt
  677.      */
  678.     public String getTitleAt(int index) {
  679.         return ((Page)pages.elementAt(index)).title;
  680.     }
  681.  
  682.     /**
  683.      * Returns the tab icon at <i>index</i>.
  684.      *
  685.      * @see #setIconAt
  686.      */
  687.     public Icon getIconAt(int index) {
  688.         return ((Page)pages.elementAt(index)).icon;
  689.     }
  690.  
  691.     /**
  692.      * Returns the tab disabled icon at <i>index</i>.
  693.      *
  694.      * @see #setDisabledIconAt
  695.      */
  696.     public Icon getDisabledIconAt(int index) {
  697.         return ((Page)pages.elementAt(index)).disabledIcon;
  698.     }
  699.  
  700.     /**
  701.      * Returns the tab background color at <i>index</i>.
  702.      *
  703.      * @see #setBackgroundAt
  704.      */
  705.     public Color getBackgroundAt(int index) {
  706.         return ((Page)pages.elementAt(index)).getBackground();
  707.     }
  708.  
  709.     /**
  710.      * Returns the tab foreground color at <i>index</i>.
  711.      *
  712.      * @see #setForegroundAt
  713.      */
  714.     public Color getForegroundAt(int index) {
  715.         return ((Page)pages.elementAt(index)).getForeground();
  716.     }
  717.  
  718.     /**
  719.      * Returns whether or not the tab at <i>index</i> is
  720.      * currently enabled.
  721.      *
  722.      * @see #setEnabledAt
  723.      */
  724.     public boolean isEnabledAt(int index) {
  725.         return ((Page)pages.elementAt(index)).isEnabled();
  726.     }
  727.  
  728.     /**
  729.      * Returns the component at <i>index</i>.
  730.      *
  731.      * @see #setComponentAt
  732.      */
  733.     public Component getComponentAt(int index) {
  734.         return ((Page)pages.elementAt(index)).component;
  735.     }
  736.  
  737.     /**
  738.      * Returns the tab bounds at <i>index</i>.  If the tab at
  739.      * this index is not currently visible in the UI, then returns null.
  740.      * If there is no UI set on this tabbedpane, then returns null.
  741.      */
  742.     public Rectangle getBoundsAt(int index) {
  743.         if (ui != null) {
  744.             return ((TabbedPaneUI)ui).getTabBounds(this, index);
  745.         }
  746.         return null;
  747.     }  
  748.  
  749. // Setters for the Pages
  750.  
  751.     /**
  752.      * Sets the title at <i>index</i> to <i>title</i> which can be null. 
  753.      * An internal exception is raised if there is no tab at that index.
  754.      * @param index the tab index where the title should be set 
  755.      * @param title the title to be displayed in the tab
  756.      *
  757.      * @see #getTitleAt
  758.      * @beaninfo
  759.      *    preferred: true
  760.      *    attribute: visualUpdate true
  761.      *  description: The title at the specified tab index.
  762.      */
  763.     public void setTitleAt(int index, String title) {
  764.         String oldTitle =((Page)pages.elementAt(index)).title;
  765.         ((Page)pages.elementAt(index)).title = title;
  766.  
  767.         if ((oldTitle != title) && (accessibleContext != null)) {
  768.             accessibleContext.firePropertyChange(
  769.                     AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 
  770.                     oldTitle, title);
  771.         }
  772.         if (title == null || oldTitle == null ||
  773.             !title.equals(oldTitle)) {
  774.             revalidate();
  775.             repaint();
  776.         }
  777.     }
  778.  
  779.     /**
  780.      * Sets the icon at <i>index</i> to <i>icon</i> which can be null.
  781.      * An internal exception is raised if there is no tab at that index. 
  782.      * @param index the tab index where the icon should be set 
  783.      * @param icon the icon to be displayed in the tab
  784.      *
  785.      * @see #getIconAt
  786.      * @beaninfo
  787.      *    preferred: true
  788.      *    attribute: visualUpdate true
  789.      *  description: The icon at the specified tab index.
  790.      */
  791.     public void setIconAt(int index, Icon icon) {
  792.         Icon oldIcon = ((Page)pages.elementAt(index)).icon;
  793.         ((Page)pages.elementAt(index)).icon = icon;
  794.  
  795.         AccessibleContext ac = getAccessibleContext();
  796.         // Fire the accessibility Visible data change
  797.         if ((oldIcon != icon) && (accessibleContext != null)) {
  798.             accessibleContext.firePropertyChange(
  799.                     AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 
  800.                     oldIcon, icon);
  801.         }
  802.         if (icon != oldIcon) {
  803.             revalidate();
  804.             repaint();
  805.         }
  806.     }
  807.  
  808.     /**
  809.      * Sets the disabled icon at <i>index</i> to <i>icon</i> which can be null.
  810.      * An internal exception is raised if there is no tab at that index. 
  811.      * @param index the tab index where the disabled icon should be set 
  812.      * @param icon the icon to be displayed in the tab when disabled
  813.      *
  814.      * @see #getDisabledIconAt
  815.      * @beaninfo
  816.      *    preferred: true
  817.      *    attribute: visualUpdate true
  818.      *  description: The disabled icon at the specified tab index.
  819.      */
  820.     public void setDisabledIconAt(int index, Icon disabledIcon) {
  821.         Icon oldIcon = ((Page)pages.elementAt(index)).disabledIcon;
  822.         ((Page)pages.elementAt(index)).disabledIcon = disabledIcon;
  823.         if (disabledIcon != oldIcon && !isEnabledAt(index)) {
  824.             revalidate();
  825.             repaint();
  826.         }
  827.     }
  828.  
  829.     /**
  830.      * Sets the background color at <i>index</i> to <i>background</i> 
  831.      * which can be null, in which case the tab's background color
  832.      * will default to the background color of the tabbedpane.
  833.      * An internal exception is raised if there is no tab at that index.
  834.      * @param index the tab index where the background should be set 
  835.      * @param background the color to be displayed in the tab's background
  836.      *
  837.      * @see #getBackgroundAt
  838.      * @beaninfo
  839.      *    preferred: true
  840.      *    attribute: visualUpdate true
  841.      *  description: The background color at the specified tab index.
  842.      */
  843.     public void setBackgroundAt(int index, Color background) {
  844.         Color oldBg = ((Page)pages.elementAt(index)).background;
  845.         ((Page)pages.elementAt(index)).setBackground(background);
  846.         if (background == null || oldBg == null ||
  847.             !background.equals(oldBg)) {
  848.             Rectangle tabBounds = getBoundsAt(index);
  849.             if (tabBounds != null) {
  850.                 repaint(tabBounds);
  851.             }
  852.         }
  853.     }
  854.  
  855.     /**
  856.      * Sets the foreground color at <i>index</i> to <i>foreground</i> 
  857.      * which can be null, in which case the tab's foreground color
  858.      * will default to the foreground color of this tabbedpane.
  859.      * An internal exception is raised if there is no tab at that index. 
  860.      * @param index the tab index where the foreground should be set 
  861.      * @param foreground the color to be displayed as the tab's foreground
  862.      *
  863.      * @see #getForegroundAt
  864.      * @beaninfo
  865.      *    preferred: true
  866.      *    attribute: visualUpdate true
  867.      *  description: The foreground color at the specified tab index.
  868.      */
  869.     public void setForegroundAt(int index, Color foreground) {
  870.         Color oldFg = ((Page)pages.elementAt(index)).foreground;
  871.         ((Page)pages.elementAt(index)).setForeground(foreground);
  872.         if (foreground == null || oldFg == null ||
  873.             !foreground.equals(oldFg)) {
  874.             Rectangle tabBounds = getBoundsAt(index);
  875.             if (tabBounds != null) {
  876.                 repaint(tabBounds);
  877.             }
  878.         }
  879.     }
  880.  
  881.     /**
  882.      * Sets whether or not the tab at <i>index</i> is enabled.
  883.      * An internal exception is raised if there is no tab at that index.
  884.      * @param index the tab index which should be enabled/disabled
  885.      * @param enabled whether or not the tab should be enabled
  886.      *
  887.      * @see #isEnabledAt 
  888.      */
  889.     public void setEnabledAt(int index, boolean enabled) {
  890.         boolean oldEnabled = ((Page)pages.elementAt(index)).isEnabled();
  891.         ((Page)pages.elementAt(index)).setEnabled(enabled);
  892.         if (enabled != oldEnabled) {
  893.             repaint(getBoundsAt(index));
  894.         }
  895.     }
  896.  
  897.     /**
  898.      * Sets the component at <i>index</i> to <i>component</i>.  
  899.      * An internal exception is raised if there is no tab at that index.
  900.      * @param index the tab index where this component is being placed
  901.      * @param component the component for the tab
  902.      *
  903.      * @see #getComponentAt  
  904.      * @beaninfo
  905.      *    attribute: visualUpdate true
  906.      *  description: The component at the specified tab index.
  907.      */
  908.     public void setComponentAt(int index, Component component) {
  909.         Page page = (Page)pages.elementAt(index);
  910.         if (component != page.component) {
  911.             if (page.component != null) {
  912.                 synchronized(getTreeLock()) {
  913.                     int count = getComponentCount();
  914.                     Component children[] = getComponents();
  915.                     for (int i = 0; i < count; i++) {
  916.                         if (children[i] == page.component) {
  917.                             remove(i);
  918.                         }
  919.                     }
  920.                 }
  921.             }
  922.             page.component = component;
  923.             component.setVisible(getSelectedIndex() == index);
  924.             addImpl(component, null, -1);
  925.             revalidate();
  926.         }
  927.     }
  928.  
  929.     /**
  930.      * Returns the first tab index with a given <i>title</i>, 
  931.      * Returns -1 if no tab has this title. 
  932.      * @param title the title for the tab
  933.      */
  934.     public int indexOfTab(String title) {
  935.         for(int i = 0; i < getTabCount(); i++) { 
  936.             if (getTitleAt(i).equals(title == null? "" : title)) { 
  937.                 return i;
  938.             }
  939.         }
  940.         return -1; 
  941.     }
  942.  
  943.     /**
  944.      * Returns the first tab index with a given <i>icon</i>.
  945.      * Returns -1 if no tab has this icon.
  946.      * @param icon the icon for the tab
  947.      */
  948.     public int indexOfTab(Icon icon) {
  949.         for(int i = 0; i < getTabCount(); i++) { 
  950.             if (getIconAt(i).equals(icon)) { 
  951.                 return i;
  952.             }
  953.         }
  954.         return -1; 
  955.     }
  956.  
  957.     /**
  958.      * Returns the index of the tab for the specified component.
  959.      * Returns -1 if there is no tab for this component.
  960.      * @param component the component for the tab
  961.      */
  962.     public int indexOfComponent(Component component) {
  963.         for(int i = 0; i < getTabCount(); i++) { 
  964.             if (getComponentAt(i).equals(component)) { 
  965.                 return i;
  966.             }
  967.         }
  968.         return -1; 
  969.     }
  970.  
  971.     /**
  972.      * Returns the tooltip text for the component determined by the
  973.      * mouse event location.
  974.      *
  975.      * @param event  the MouseEvent that tells where the cursor is lingering
  976.      * @return the String with the tooltip text
  977.      */
  978.     public String getToolTipText(MouseEvent event) {
  979.         if (ui != null) {
  980.             int index = ((TabbedPaneUI)ui).tabForCoordinate(this, event.getX(), event.getY());
  981.  
  982.             if (index != -1) {
  983.                 return ((Page)pages.elementAt(index)).tip;
  984.             }
  985.         }
  986.         return super.getToolTipText(event);
  987.     }
  988.  
  989.  
  990.     /** 
  991.      * See readObject() and writeObject() in JComponent for more 
  992.      * information about serialization in Swing.
  993.      */
  994.     private void writeObject(ObjectOutputStream s) throws IOException {
  995.         s.defaultWriteObject();
  996.     if ((ui != null) && (getUIClassID().equals(uiClassID))) {
  997.         ui.installUI(this);
  998.     }
  999.     }
  1000.  
  1001.  
  1002.     /**
  1003.      * Returns a string representation of this JTabbedPane. This method 
  1004.      * is intended to be used only for debugging purposes, and the 
  1005.      * content and format of the returned string may vary between      
  1006.      * implementations. The returned string may be empty but may not 
  1007.      * be <code>null</code>.
  1008.      * <P>
  1009.      * Overriding paramString() to provide information about the
  1010.      * specific new aspects of the JFC components.
  1011.      * 
  1012.      * @return  a string representation of this JTabbedPane.
  1013.      */
  1014.     protected String paramString() {
  1015.         String tabPlacementString;
  1016.         if (tabPlacement == TOP) {
  1017.             tabPlacementString = "TOP";
  1018.         } else if (tabPlacement == BOTTOM) {
  1019.             tabPlacementString = "BOTTOM";
  1020.         } else if (tabPlacement == LEFT) {
  1021.             tabPlacementString = "LEFT";
  1022.         } else if (tabPlacement == RIGHT) {
  1023.             tabPlacementString = "RIGHT";
  1024.         } else tabPlacementString = "";
  1025.         String haveRegisteredString = (haveRegistered ?
  1026.                        "true" : "false");
  1027.  
  1028.     return super.paramString() +
  1029.         ",haveRegistered=" + haveRegisteredString +
  1030.         ",tabPlacement=" + tabPlacementString;
  1031.     }
  1032.  
  1033. /////////////////
  1034. // Accessibility support
  1035. ////////////////
  1036.  
  1037.     /**
  1038.      * Get the AccessibleContext associated with this JComponent
  1039.      *
  1040.      * @return the AccessibleContext of this JComponent
  1041.      */
  1042.     public AccessibleContext getAccessibleContext() {
  1043.         if (accessibleContext == null) {
  1044.             accessibleContext = new AccessibleJTabbedPane();
  1045.         }
  1046.         return accessibleContext;
  1047.     }
  1048.  
  1049.     /**
  1050.      * The class used to obtain the accessible role for this object.
  1051.      * <p>
  1052.      * <strong>Warning:</strong>
  1053.      * Serialized objects of this class will not be compatible with
  1054.      * future Swing releases.  The current serialization support is appropriate
  1055.      * for short term storage or RMI between applications running the same
  1056.      * version of Swing.  A future release of Swing will provide support for
  1057.      * long term persistence.
  1058.      */
  1059.     protected class AccessibleJTabbedPane extends AccessibleJComponent 
  1060.         implements AccessibleSelection, ChangeListener {
  1061.  
  1062.         /**
  1063.          *  Constructs an AccessibleJTabbedPane
  1064.          */
  1065.         public AccessibleJTabbedPane() {
  1066.             super();
  1067.             JTabbedPane.this.model.addChangeListener(this);
  1068.         }
  1069.  
  1070.         public void stateChanged(ChangeEvent e) {
  1071.             Object o = e.getSource();
  1072.             firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
  1073.                                null, o);
  1074.         }
  1075.  
  1076.         /**
  1077.          * Get the role of this object.
  1078.          *
  1079.          * @return an instance of AccessibleRole describing the role of 
  1080.          *          the object
  1081.          */
  1082.         public AccessibleRole getAccessibleRole() {
  1083.             return AccessibleRole.PAGE_TAB_LIST;
  1084.         }
  1085.  
  1086.         /**
  1087.          * Returns the number of accessible children in the object.
  1088.          *
  1089.          * @return the number of accessible children in the object.
  1090.          */
  1091.         public int getAccessibleChildrenCount() {
  1092.             return getTabCount();
  1093.         }
  1094.  
  1095.         /**
  1096.          * Return the specified Accessible child of the object.
  1097.          *
  1098.          * @param i zero-based index of child
  1099.          * @return the Accessible child of the object
  1100.          */
  1101.         public Accessible getAccessibleChild(int i) {
  1102.             if (i < 0 || i >= getTabCount()) {
  1103.                 return null;
  1104.             }
  1105.             return (Accessible) pages.elementAt(i);
  1106.         }
  1107.  
  1108.         /**
  1109.          * Get the AccessibleSelection associated with this object if one
  1110.          * exists.  Otherwise return null.
  1111.          *
  1112.          * @return the AccessibleSelection, or null
  1113.          */
  1114.         public AccessibleSelection getAccessibleSelection() {
  1115.            return this;
  1116.         }
  1117.  
  1118.         public Accessible getAccessibleAt(Point p) {
  1119.             int tab = ((TabbedPaneUI) ui).tabForCoordinate(JTabbedPane.this, 
  1120.                                                            p.x, p.y);
  1121.             if (tab == -1) {
  1122.                 tab = getSelectedIndex();
  1123.             }
  1124.             return getAccessibleChild(tab);
  1125.         }
  1126.  
  1127.         public int getAccessibleSelectionCount() {
  1128.             return 1;
  1129.         }
  1130.  
  1131.         public Accessible getAccessibleSelection(int i) {
  1132.             int index = getSelectedIndex();
  1133.             if (index == -1) {
  1134.                 return null;
  1135.             }
  1136.             return (Accessible) pages.elementAt(index);
  1137.         }
  1138.  
  1139.         public boolean isAccessibleChildSelected(int i) {
  1140.             return (i == getSelectedIndex());
  1141.         }
  1142.  
  1143.         public void addAccessibleSelection(int i) {
  1144.            setSelectedIndex(i);
  1145.         }
  1146.  
  1147.         public void removeAccessibleSelection(int i) {
  1148.            // can't do
  1149.         }
  1150.  
  1151.         public void clearAccessibleSelection() {
  1152.            // can't do
  1153.         }
  1154.  
  1155.         public void selectAllAccessibleSelection() {
  1156.            // can't do
  1157.         }
  1158.     }
  1159.  
  1160.     private class Page extends AccessibleContext 
  1161.         implements Serializable, Accessible, AccessibleComponent {
  1162.         String title;
  1163.         Color background;
  1164.         Color foreground;
  1165.         Icon icon;
  1166.         Icon disabledIcon;
  1167.         JTabbedPane parent;
  1168.         Component component;
  1169.         String tip;
  1170.         boolean enabled = true;
  1171.         boolean needsUIUpdate;
  1172.  
  1173.         Page(JTabbedPane parent, 
  1174.              String title, Icon icon, Icon disabledIcon, Component component, String tip) {
  1175.             this.title = title;
  1176.             this.icon = icon;
  1177.             this.disabledIcon = disabledIcon;
  1178.             this.parent = parent;
  1179.             this.setAccessibleParent(parent);
  1180.             this.component = component;
  1181.             this.tip = tip;
  1182.             if (component instanceof Accessible) {
  1183.                 AccessibleContext ac;
  1184.                 ac = ((Accessible) component).getAccessibleContext();
  1185.                 if (ac != null) {
  1186.                     ac.setAccessibleParent(this);
  1187.                 }
  1188.             }
  1189.         }
  1190.  
  1191.         /////////////////
  1192.         // Accessibility support
  1193.         ////////////////
  1194.  
  1195.         public AccessibleContext getAccessibleContext() {
  1196.             return this;
  1197.         }
  1198.  
  1199.  
  1200.         // AccessibleContext methods
  1201.  
  1202.         public String getAccessibleName() {
  1203.             if (accessibleName != null) {
  1204.                 return accessibleName;
  1205.             } else if (title != null) {
  1206.                 return title;
  1207.             }
  1208.             return null;
  1209.         }
  1210.  
  1211.         public String getAccessibleDescription() {
  1212.             if (accessibleDescription != null) {
  1213.                 return accessibleDescription;
  1214.             } else if (tip != null) {
  1215.                 return tip;
  1216.             }
  1217.             return null;
  1218.         }
  1219.  
  1220.         public AccessibleRole getAccessibleRole() {
  1221.             return AccessibleRole.PAGE_TAB;
  1222.         }
  1223.  
  1224.         public AccessibleStateSet getAccessibleStateSet() {
  1225.             AccessibleStateSet states;
  1226.             states = parent.getAccessibleContext().getAccessibleStateSet();
  1227.             states.add(AccessibleState.SELECTABLE);
  1228.             int i = parent.indexOfTab(title);
  1229.             if (i == parent.getSelectedIndex()) {
  1230.                 states.add(AccessibleState.SELECTED);
  1231.             }       
  1232.             return states;
  1233.         }
  1234.  
  1235.         public int getAccessibleIndexInParent() {
  1236.             return parent.indexOfTab(title);
  1237.         }
  1238.  
  1239.         public int getAccessibleChildrenCount() {
  1240.             if (component instanceof Accessible) {
  1241.                 return 1;
  1242.             } else {
  1243.                 return 0;
  1244.             }
  1245.         }       
  1246.  
  1247.         public Accessible getAccessibleChild(int i) {
  1248.             if (component instanceof Accessible) {
  1249.                 return (Accessible) component;
  1250.             } else {
  1251.                 return null;
  1252.             }
  1253.         }
  1254.  
  1255.         public Locale getLocale() {
  1256.             return parent.getLocale();
  1257.         }
  1258.  
  1259.         public AccessibleComponent getAccessibleComponent() {
  1260.             return this;
  1261.         }
  1262.  
  1263.  
  1264.         // AccessibleComponent methods
  1265.  
  1266.         public Color getBackground() {
  1267.             return background != null? background : parent.getBackground();
  1268.         }
  1269.  
  1270.         public void setBackground(Color c) {
  1271.             background = c;
  1272.         }
  1273.  
  1274.         public Color getForeground() {
  1275.             return foreground != null? foreground : parent.getForeground();
  1276.         }
  1277.  
  1278.         public void setForeground(Color c) {
  1279.             foreground = c;
  1280.         }
  1281.  
  1282.         public Cursor getCursor() {
  1283.             return parent.getCursor();
  1284.         }
  1285.  
  1286.         public void setCursor(Cursor c) {
  1287.             parent.setCursor(c);
  1288.         }
  1289.  
  1290.         public Font getFont() {
  1291.             return parent.getFont();
  1292.         }
  1293.  
  1294.         public void setFont(Font f) {
  1295.             parent.setFont(f);
  1296.         }
  1297.  
  1298.         public FontMetrics getFontMetrics(Font f) {
  1299.             return parent.getFontMetrics(f);
  1300.         }
  1301.  
  1302.         public boolean isEnabled() {
  1303.             return enabled;
  1304.         }
  1305.  
  1306.         public void setEnabled(boolean b) {
  1307.             enabled = b;
  1308.         }
  1309.  
  1310.         public boolean isVisible() {
  1311.             return parent.isVisible();
  1312.         }
  1313.  
  1314.         public void setVisible(boolean b) {
  1315.             parent.setVisible(b);
  1316.         }
  1317.  
  1318.         public boolean isShowing() {
  1319.             return parent.isShowing();
  1320.         }
  1321.  
  1322.         public boolean contains(Point p) {
  1323.             Rectangle r = getBounds();
  1324.             return r.contains(p);
  1325.         }
  1326.  
  1327.         public Point getLocationOnScreen() {
  1328.              Point parentLocation = parent.getLocationOnScreen();
  1329.              Point componentLocation = getLocation();
  1330.              componentLocation.translate(parentLocation.x, parentLocation.y);
  1331.              return componentLocation;
  1332.         }
  1333.     
  1334.         public Point getLocation() {
  1335.              Rectangle r = getBounds();
  1336.              return new Point(r.x, r.y);
  1337.         }
  1338.     
  1339.         public void setLocation(Point p) {
  1340.             // do nothing
  1341.         }
  1342.  
  1343.         public Rectangle getBounds() {
  1344.             return parent.getUI().getTabBounds(parent, 
  1345.                                                parent.indexOfTab(title));
  1346.         }
  1347.  
  1348.         public void setBounds(Rectangle r) {
  1349.             // do nothing
  1350.         }
  1351.  
  1352.         public Dimension getSize() {
  1353.             Rectangle r = getBounds();
  1354.             return new Dimension(r.width, r.height);
  1355.         }
  1356.  
  1357.         public void setSize(Dimension d) {
  1358.             // do nothing
  1359.         }
  1360.  
  1361.         public Accessible getAccessibleAt(Point p) {
  1362.             if (component instanceof Accessible) {
  1363.                 return (Accessible) component;
  1364.             } else {
  1365.                 return null;
  1366.             }
  1367.         }
  1368.  
  1369.         public boolean isFocusTraversable() {
  1370.             return false;
  1371.         }
  1372.  
  1373.         public void requestFocus() {
  1374.             // do nothing
  1375.         }
  1376.  
  1377.         public void addFocusListener(FocusListener l) {
  1378.             // do nothing
  1379.         }
  1380.  
  1381.         public void removeFocusListener(FocusListener l) {
  1382.             // do nothing
  1383.         }
  1384.     }
  1385. }
  1386.